Web Audio API๋ฅผ ํตํด ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์๊ฐ ์ค๋์ค ์กฐ์์ ํ์ ํ์ฉํ์ธ์. ์ด ์ข ํฉ ๊ฐ์ด๋๋ ์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ๊ตฌํ, ๊ฐ๋ ๋ฐ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ๋ค๋ฃน๋๋ค.
ํ๋ก ํธ์๋ ์ค๋์ค ์ฒ๋ฆฌ: Web Audio API ๋ง์คํฐํ๊ธฐ
์ค๋๋ ์ญ๋์ ์ธ ์น ํ๊ฒฝ์์ ์ธํฐ๋ํฐ๋ธํ๊ณ ๋งค๋ ฅ์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ด ๊ฐ์ฅ ์ค์ํฉ๋๋ค. ์๊ฐ์ ์ธ ํ๋ คํจ ์ธ์๋ ์ฒญ๊ฐ ์์๋ ๋ชฐ์ ๊ฐ ์๊ณ ๊ธฐ์ต์ ๋จ๋ ๋์งํธ ์ํธ ์์ฉ์ ๋ง๋๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ๊ฐ๋ ฅํ JavaScript API์ธ Web Audio API๋ ๊ฐ๋ฐ์์๊ฒ ๋ธ๋ผ์ฐ์ ๋ด์์ ์ง์ ์ค๋์ค ์ฝํ ์ธ ๋ฅผ ์์ฑ, ์ฒ๋ฆฌ ๋ฐ ๋๊ธฐํํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ์ข ํฉ ๊ฐ์ด๋๋ Web Audio API์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ๊ตฌํ์ ์๋ดํ์ฌ ์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์ ๊ตํ ์ค๋์ค ๊ฒฝํ์ ๋ง๋ค ์ ์๋๋ก ์ง์ํฉ๋๋ค.
Web Audio API๋ ๋ฌด์์ ๋๊น?
Web Audio API๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์์ ์ค๋์ค๋ฅผ ์ฒ๋ฆฌํ๊ณ ํฉ์ฑํ๊ธฐ ์ํด ์ค๊ณ๋ ๊ณ ๊ธ JavaScript API์
๋๋ค. ์ค๋์ค ์์ค, ํจ๊ณผ ๋ฐ ๋์์ด ์ฐ๊ฒฐ๋์ด ๋ณต์กํ ์ค๋์ค ํ์ดํ๋ผ์ธ์ ์์ฑํ๋ ๋ชจ๋์ ๊ทธ๋ํ ๊ธฐ๋ฐ ์ํคํ
์ฒ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ฃผ๋ก ์ฌ์์ ์ํ ๊ธฐ๋ณธ <audio> ๋ฐ <video> ์์์ ๋ฌ๋ฆฌ Web Audio API๋ ์ค๋์ค ์ ํธ์ ๋ํ ์ธ๋ฐํ ์ ์ด๋ฅผ ์ ๊ณตํ์ฌ ์ค์๊ฐ ์กฐ์, ํฉ์ฑ ๋ฐ ์ ๊ตํ ํจ๊ณผ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
API๋ ๋ช ๊ฐ์ง ์ฃผ์ ๊ตฌ์ฑ ์์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋์์ต๋๋ค.
- AudioContext: ๋ชจ๋ ์ค๋์ค ์์ ์ ์ค์ ํ๋ธ์ ๋๋ค. ์ค๋์ค ์ฒ๋ฆฌ ๊ทธ๋ํ๋ฅผ ๋ํ๋ด๋ฉฐ ๋ชจ๋ ์ค๋์ค ๋ ธ๋๋ฅผ ๋ง๋๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- ์ค๋์ค ๋ ธ๋: ์ค๋์ค ๊ทธ๋ํ์ ๋น๋ฉ ๋ธ๋ก์ ๋๋ค. ๋ฐ์ง๊ธฐ ๋๋ ๋ง์ดํฌ ์ ๋ ฅ๊ณผ ๊ฐ์ ์์ค, ํํฐ ๋๋ ์ง์ฐ๊ณผ ๊ฐ์ ํจ๊ณผ, ์คํผ์ปค ์ถ๋ ฅ๊ณผ ๊ฐ์ ๋์์ ๋ํ๋ ๋๋ค.
- ์ฐ๊ฒฐ: ๋ ธ๋๋ ์ค๋์ค ์ฒ๋ฆฌ ์ฒด์ธ์ ํ์ฑํ๊ธฐ ์ํด ์ฐ๊ฒฐ๋ฉ๋๋ค. ๋ฐ์ดํฐ๋ ์์ค ๋ ธ๋์์ ํจ๊ณผ ๋ ธ๋๋ฅผ ๊ฑฐ์ณ ๋์ ๋ ธ๋๋ก ํ๋ฆ ๋๋ค.
์์ํ๊ธฐ: AudioContext
์ค๋์ค๋ก ์์
์ ์์ํ๊ธฐ ์ ์ AudioContext ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ์ ์ฒด Web Audio API์ ์ง์
์ ์
๋๋ค.
์์ : AudioContext ๋ง๋ค๊ธฐ
```javascript let audioContext; try { // Standard API */ audioContext = new (window.AudioContext || window.webkitAudioContext)(); console.log('AudioContext๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ต๋๋ค!'); } catch (e) { // Web Audio API๋ ์ด ๋ธ๋ผ์ฐ์ ์์ ์ง์๋์ง ์์ต๋๋ค. alert('Web Audio API๋ ๋ธ๋ผ์ฐ์ ์์ ์ง์๋์ง ์์ต๋๋ค. ์ต์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ์ญ์์ค.'); } ```Chrome ๋ฐ Safari์ ์ด์ ๋ฒ์ ์์๋ ์ ๋์ฌ๊ฐ ๋ถ์ webkitAudioContext๋ฅผ ์ฌ์ฉํ์ผ๋ฏ๋ก ๋ธ๋ผ์ฐ์ ํธํ์ฑ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. AudioContext๋ ๋ธ๋ผ์ฐ์ ์๋ ์ฌ์ ์ ์ฑ
์ผ๋ก ์ธํด ์ฌ์ฉ์ ์ํธ ์์ฉ(์: ๋ฒํผ ํด๋ฆญ)์ ๋ํ ์๋ต์ผ๋ก ์์ฑํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค.
์ค๋์ค ์์ค: ์ฌ์ด๋ ์์ฑ ๋ฐ ๋ก๋ฉ
์ค๋์ค ์ฒ๋ฆฌ๋ ์ค๋์ค ์์ค์์ ์์๋ฉ๋๋ค. Web Audio API๋ ์ฌ๋ฌ ์ ํ์ ์์ค๋ฅผ ์ง์ํฉ๋๋ค.
1. OscillatorNode: ํค ํฉ์ฑ
OscillatorNode๋ ์ฃผ๊ธฐ์ ์ธ ํํ ์์ฑ๊ธฐ์
๋๋ค. ์ฌ์ธํ, ์ฌ๊ฐํ, ํฑ๋ํ ๋ฐ ์ผ๊ฐํ์ ๊ฐ์ ๊ธฐ๋ณธ ํฉ์ฑ ์ฌ์ด๋๋ฅผ ๋ง๋๋ ๋ฐ ํ์ํฉ๋๋ค.
์์ : ์ฌ์ธํ ์์ฑ ๋ฐ ์ฌ์
```javascript if (audioContext) { const oscillator = audioContext.createOscillator(); oscillator.type = 'sine'; // 'sine', 'square', 'sawtooth', 'triangle' oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // A4 ์ํ(440Hz) // ๋ฐ์ง๊ธฐ๋ฅผ ์ค๋์ค ์ปจํ ์คํธ์ ๋์(์คํผ์ปค)์ ์ฐ๊ฒฐํฉ๋๋ค. oscillator.connect(audioContext.destination); // ๋ฐ์ง๊ธฐ๋ฅผ ์์ํฉ๋๋ค. oscillator.start(); // 1์ด ํ์ ๋ฐ์ง๊ธฐ๋ฅผ ์ค์งํฉ๋๋ค. setTimeout(() => { oscillator.stop(); console.log('์ฌ์ธํ๊ฐ ์ค์ง๋์์ต๋๋ค.'); }, 1000); } ```OscillatorNode์ ์ฃผ์ ์์ฑ:
type: ํํ ๋ชจ์์ ์ค์ ํฉ๋๋ค.frequency: ํค๋ฅด์ธ (Hz) ๋จ์๋ก ํผ์น๋ฅผ ์ ์ดํฉ๋๋ค.setValueAtTime,linearRampToValueAtTime๋ฐexponentialRampToValueAtTime๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ฅธ ์ฃผํ์ ๋ณ๊ฒฝ์ ์ ํํ๊ฒ ์ ์ดํ ์ ์์ต๋๋ค.
2. BufferSourceNode: ์ค๋์ค ํ์ผ ์ฌ์
BufferSourceNode๋ AudioBuffer์ ๋ก๋๋ ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ์ฌ์ํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์งง์ ์ฌ์ด๋ ํจ๊ณผ ๋๋ ๋ฏธ๋ฆฌ ๋
น์๋ ์ค๋์ค ํด๋ฆฝ์ ์ฌ์ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
๋จผ์ ์ค๋์ค ํ์ผ์ ๊ฐ์ ธ์์ ๋์ฝ๋ฉํด์ผ ํฉ๋๋ค.
์์ : ์ค๋์ค ํ์ผ ๋ก๋ ๋ฐ ์ฌ์
```javascript async function playSoundFile(url) { if (!audioContext) return; try { const response = await fetch(url); const arrayBuffer = await response.arrayBuffer(); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(); // ์ฌ์ด๋๋ฅผ ์ฆ์ ์ฌ์ํฉ๋๋ค. console.log(`์ฌ์ด๋ ์ฌ์ ์์น: ${url}`); source.onended = () => { console.log('์ฌ์ด๋ ํ์ผ ์ฌ์์ด ์ข ๋ฃ๋์์ต๋๋ค.'); }; } catch (e) { console.error('์ค๋์ค ๋ฐ์ดํฐ ๋์ฝ๋ฉ ๋๋ ์ฌ์ ์ค๋ฅ:', e); } } // ์ฌ์ฉ ๋ฐฉ๋ฒ: // playSoundFile('path/to/your/sound.mp3'); ```AudioContext.decodeAudioData()๋ MP3, WAV, Ogg Vorbis์ ๊ฐ์ ๋ค์ํ ํ์์ ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ AudioBuffer๋ก ๋์ฝ๋ฉํ๋ ๋น๋๊ธฐ ์์
์
๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ด AudioBuffer๋ฅผ BufferSourceNode์ ํ ๋นํ ์ ์์ต๋๋ค.
3. MediaElementAudioSourceNode: HTMLMediaElement ์ฌ์ฉ
์ด ๋
ธ๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ์กด HTML <audio> ๋๋ <video> ์์๋ฅผ ์ค๋์ค ์์ค๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ํ์ค HTML ์์๋ก ์ ์ด๋๋ ๋ฏธ๋์ด์ Web Audio API ํจ๊ณผ๋ฅผ ์ ์ฉํ๋ ค๋ ๊ฒฝ์ฐ์ ์ ์ฉํฉ๋๋ค.
์์ : HTML ์ค๋์ค ์์์ ํจ๊ณผ ์ ์ฉ
```javascript // HTML์ ์ค๋์ค ์์๊ฐ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. // if (audioContext) { const audioElement = document.getElementById('myAudio'); const mediaElementSource = audioContext.createMediaElementSource(audioElement); // ์ด์ ์ด ์์ค๋ฅผ ๋ค๋ฅธ ๋ ธ๋(์: ํจ๊ณผ)์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. // ์ง๊ธ์ ๋์์ผ๋ก ์ง์ ์ฐ๊ฒฐํด ๋ณด๊ฒ ์ต๋๋ค. mediaElementSource.connect(audioContext.destination); // JavaScript๋ฅผ ํตํด ์ฌ์์ ์ ์ดํ๋ ค๋ฉด: // audioElement.play(); // audioElement.pause(); } ```์ด ์ ๊ทผ ๋ฐฉ์์ ์ค๋์ค ์ฒ๋ฆฌ ๊ทธ๋ํ์์ ์ฌ์ ์ ์ด๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค.
4. MediaStreamAudioSourceNode: ์ค์๊ฐ ์ค๋์ค ์ ๋ ฅ
navigator.mediaDevices.getUserMedia()๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ๋ง์ดํฌ ๋๋ ๊ธฐํ ๋ฏธ๋์ด ์
๋ ฅ ์ฅ์น์์ ์ค๋์ค๋ฅผ ์บก์ฒํ ์ ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ๊ฒฐ๊ณผ MediaStream์ MediaStreamAudioSourceNode๋ฅผ ์ฌ์ฉํ์ฌ Web Audio API์ ๊ณต๊ธํ ์ ์์ต๋๋ค.
์์ : ๋ง์ดํฌ ์ ๋ ฅ ์บก์ฒ ๋ฐ ์ฌ์
```javascript async function startMicInput() { if (!audioContext) return; try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const microphoneSource = audioContext.createMediaStreamSource(stream); // ์ด์ ๋ง์ดํฌ ์ ๋ ฅ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค(์: ํจ๊ณผ ๋๋ ๋์์ ์ฐ๊ฒฐ). microphoneSource.connect(audioContext.destination); console.log('๋ง์ดํฌ ์ ๋ ฅ์ด ์บก์ฒ๋์ด ์ฌ์ ์ค์ ๋๋ค.'); // ์ค์งํ๋ ค๋ฉด: // stream.getTracks().forEach(track => track.stop()); } catch (err) { console.error('๋ง์ดํฌ ์ก์ธ์ค ์ค๋ฅ:', err); alert('๋ง์ดํฌ์ ์ก์ธ์คํ ์ ์์ต๋๋ค. ๊ถํ์ ๋ถ์ฌํ์ญ์์ค.'); } } // ๋ง์ดํฌ๋ฅผ ์์ํ๋ ค๋ฉด: // startMicInput(); ```๋ง์ดํฌ์ ์ก์ธ์คํ๋ ค๋ฉด ์ฌ์ฉ์ ๊ถํ์ด ํ์ํฉ๋๋ค.
์ค๋์ค ์ฒ๋ฆฌ: ํจ๊ณผ ์ ์ฉ
Web Audio API์ ์ง์ ํ ํ์ ์ค๋์ค ์ ํธ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์์ต๋๋ค. ์ด๋ ์์ค์ ๋์ ์ฌ์ด์ ๋ค์ํ AudioNode๋ฅผ ์ฒ๋ฆฌ ๊ทธ๋ํ์ ์ฝ์
ํ์ฌ ์ํ๋ฉ๋๋ค.
1. GainNode: ๋ณผ๋ฅจ ์ ์ด
GainNode๋ ์ค๋์ค ์ ํธ์ ๋ณผ๋ฅจ์ ์ ์ดํฉ๋๋ค. ํด๋น gain ์์ฑ์ AudioParam์ด๋ฉฐ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋ณผ๋ฅจ์ ๋ถ๋๋ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
์์ : ์ฌ์ด๋ ํ์ด๋ ์ธ
```javascript // 'source'๊ฐ AudioBufferSourceNode ๋๋ OscillatorNode๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. if (audioContext && source) { const gainNode = audioContext.createGain(); gainNode.gain.setValueAtTime(0, audioContext.currentTime); // ๋ฌด์์ผ๋ก ์์ gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2); // 2์ด์ ๊ฑธ์ณ ์ต๋ ๋ณผ๋ฅจ์ผ๋ก ํ์ด๋ํฉ๋๋ค. source.connect(gainNode); gainNode.connect(audioContext.destination); source.start(); } ```2. DelayNode: ์์ฝ ๋ฐ ๋ฆฌ๋ฒ๋ธ ๋ง๋ค๊ธฐ
DelayNode๋ ์ค๋์ค ์ ํธ์ ์๊ฐ ์ง์ฐ์ ๋์
ํฉ๋๋ค. DelayNode์ ์ถ๋ ฅ์ ์
๋ ฅ์ผ๋ก ๋ค์ ํผ๋๋ฐฑํ๋ฉด(์ข
์ข
๊ฐ์ด 1๋ณด๋ค ์์ GainNode๋ฅผ ํตํด) ์์ฝ ํจ๊ณผ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ฌ๋ฌ ์ง์ฐ๊ณผ ํํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋ณต์กํ ๋ฆฌ๋ฒ๋ธ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
์์ : ๊ฐ๋จํ ์์ฝ ๋ง๋ค๊ธฐ
```javascript // 'source'๊ฐ AudioBufferSourceNode ๋๋ OscillatorNode๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. if (audioContext && source) { const delayNode = audioContext.createDelay(); delayNode.delayTime.setValueAtTime(0.5, audioContext.currentTime); // 0.5์ด ์ง์ฐ const feedbackGain = audioContext.createGain(); feedbackGain.gain.setValueAtTime(0.3, audioContext.currentTime); // 30% ํผ๋๋ฐฑ source.connect(audioContext.destination); source.connect(delayNode); delayNode.connect(feedbackGain); feedbackGain.connect(delayNode); // ํผ๋๋ฐฑ ๋ฃจํ feedbackGain.connect(audioContext.destination); // ์ง์ ์ ํธ๋ ์ถ๋ ฅ์ผ๋ก ์ด๋ํฉ๋๋ค. source.start(); } ```3. BiquadFilterNode: ์ฃผํ์ ์ ฐ์ดํ
BiquadFilterNode๋ ์ค๋์ค ์ ํธ์ 2์ฐจ ๋ฐ์ด์ฟผ๋ ํํฐ๋ฅผ ์ ์ฉํฉ๋๋ค. ์ด๋ฌํ ํํฐ๋ ์ฃผํ์ ์ฝํ
์ธ ๋ฅผ ์
ฐ์ดํํ๊ณ ์ดํ๋ผ์ด์ ์ด์
(EQ) ํจ๊ณผ๋ฅผ ๋ง๋ค๊ณ ๊ณต์ง ์ฌ์ด๋๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ์ค๋์ค ์ฒ๋ฆฌ์ ๊ธฐ๋ณธ ์์์
๋๋ค.
์ผ๋ฐ์ ์ธ ํํฐ ์ ํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
lowpass: ๋ฎ์ ์ฃผํ์๊ฐ ํต๊ณผํ๋๋ก ํฉ๋๋ค.highpass: ๋์ ์ฃผํ์๊ฐ ํต๊ณผํ๋๋ก ํฉ๋๋ค.bandpass: ํน์ ๋ฒ์ ๋ด์ ์ฃผํ์๊ฐ ํต๊ณผํ๋๋ก ํฉ๋๋ค.lowshelf: ํน์ ์ง์ ์๋์ ์ฃผํ์๋ฅผ ๋ถ์คํธํ๊ฑฐ๋ ์ปทํฉ๋๋ค.highshelf: ํน์ ์ง์ ์์ ์ฃผํ์๋ฅผ ๋ถ์คํธํ๊ฑฐ๋ ์ปทํฉ๋๋ค.peaking: ์ค์ฌ ์ฃผํ์ ์ฃผ๋ณ์ ์ฃผํ์๋ฅผ ๋ถ์คํธํ๊ฑฐ๋ ์ปทํฉ๋๋ค.notch: ํน์ ์ฃผํ์๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
์์ : ๋ก์ฐํจ์ค ํํฐ ์ ์ฉ
```javascript // 'source'๊ฐ AudioBufferSourceNode ๋๋ OscillatorNode๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. if (audioContext && source) { const filterNode = audioContext.createBiquadFilter(); filterNode.type = 'lowpass'; // ๋ก์ฐํจ์ค ํํฐ ์ ์ฉ filterNode.frequency.setValueAtTime(1000, audioContext.currentTime); // ์ฐจ๋จ ์ฃผํ์ 1000Hz filterNode.Q.setValueAtTime(1, audioContext.currentTime); // ๊ณต์ง ๊ณ์ source.connect(filterNode); filterNode.connect(audioContext.destination); source.start(); } ```4. ConvolverNode: ์ฌ์ค์ ์ธ ๋ฆฌ๋ฒ๋ธ ๋ง๋ค๊ธฐ
ConvolverNode๋ ์ค๋์ค ์ ํธ์ ์ํ์ค ์๋ต(IR)์ ์ ์ฉํฉ๋๋ค. ์ค์ ์ํฅ ๊ณต๊ฐ(์: ๋ฐฉ ๋๋ ํ)์ ๋ฏธ๋ฆฌ ๋
น์๋ ์ค๋์ค ํ์ผ์ ์ฌ์ฉํ์ฌ ์ฌ์ค์ ์ธ ์ํฅ ํจ๊ณผ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
์์ : ์ฌ์ด๋์ ๋ฆฌ๋ฒ๋ธ ์ ์ฉ
```javascript async function applyReverb(source, reverbImpulseResponseUrl) { if (!audioContext) return; try { // ์ํ์ค ์๋ต ๋ก๋ const irResponse = await fetch(reverbImpulseResponseUrl); const irArrayBuffer = await irResponse.arrayBuffer(); const irAudioBuffer = await audioContext.decodeAudioData(irArrayBuffer); const convolver = audioContext.createConvolver(); convolver.buffer = irAudioBuffer; source.connect(convolver); convolver.connect(audioContext.destination); console.log('๋ฆฌ๋ฒ๋ธ๊ฐ ์ ์ฉ๋์์ต๋๋ค.'); } catch (e) { console.error('๋ฆฌ๋ฒ๋ธ ๋ก๋ ๋๋ ์ ์ฉ ์ค๋ฅ:', e); } } // 'myBufferSource'๊ฐ ์์๋ BufferSourceNode๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. // applyReverb(myBufferSource, 'path/to/your/reverb.wav'); ```๋ฆฌ๋ฒ๋ธ์ ํ์ง์ ์ํ์ค ์๋ต ์ค๋์ค ํ์ผ์ ํ์ง๊ณผ ํน์ฑ์ ํฌ๊ฒ ์ข์ฐ๋ฉ๋๋ค.
๊ธฐํ ์ ์ฉํ ๋ ธ๋
AnalyserNode: ์๊ฐํ์ ์ค์ํ ์ค๋์ค ์ ํธ์ ์ค์๊ฐ ์ฃผํ์ ๋ฐ ์๊ฐ ์์ญ ๋ถ์์ ์ํ ๋ ธ๋์ ๋๋ค.DynamicsCompressorNode: ์ค๋์ค ์ ํธ์ ๋์ ๋ฒ์๋ฅผ ์ค์ ๋๋ค.WaveShaperNode: ์๊ณก ๋ฐ ๊ธฐํ ๋น์ ํ ํจ๊ณผ๋ฅผ ์ ์ฉํฉ๋๋ค.PannerNode: 3D ๊ณต๊ฐ ์ค๋์ค ํจ๊ณผ๋ฅผ ์ ์ฉํฉ๋๋ค.
๋ณต์กํ ์ค๋์ค ๊ทธ๋ํ ๋น๋
Web Audio API์ ๊ฐ์ ์ ์ด๋ฌํ ๋ ธ๋๋ฅผ ํจ๊ป ์ฐ๊ฒฐํ์ฌ ๋ณต์กํ ์ค๋์ค ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ ๋ง๋ค ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ์ผ๋ฐ์ ์ธ ํจํด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
SourceNode -> EffectNode1 -> EffectNode2 -> ... -> DestinationNode
์์ : ๊ฐ๋จํ ํจ๊ณผ ์ฒด์ธ(ํํฐ์ ๊ฒ์ธ์ด ์๋ ๋ฐ์ง๊ธฐ)
```javascript if (audioContext) { const oscillator = audioContext.createOscillator(); const filter = audioContext.createBiquadFilter(); const gain = audioContext.createGain(); // ๋ ธ๋ ๊ตฌ์ฑ oscillator.type = 'sawtooth'; oscillator.frequency.setValueAtTime(220, audioContext.currentTime); // A3 ์ํ filter.type = 'bandpass'; filter.frequency.setValueAtTime(500, audioContext.currentTime); filter.Q.setValueAtTime(5, audioContext.currentTime); // ํํ๋ ์๋ฆฌ๋ฅผ ๋ด๊ธฐ ์ํ ๋์ ๊ณต์ง gain.gain.setValueAtTime(0.5, audioContext.currentTime); // ์ ๋ฐ ๋ณผ๋ฅจ // ๋ ธ๋ ์ฐ๊ฒฐ oscillator.connect(filter); filter.connect(gain); gain.connect(audioContext.destination); // ์ฌ์ ์์ oscillator.start(); // ๋ช ์ด ํ์ ์ค์ง setTimeout(() => { oscillator.stop(); console.log('ํจ๊ณผ๊ฐ ์ ์ฉ๋ ํฑ๋ํ๊ฐ ์ค์ง๋์์ต๋๋ค.'); }, 3000); } ```ํ๋์ ๋ ธ๋ ์ถ๋ ฅ์ ์ฌ๋ฌ ๋ค๋ฅธ ๋ ธ๋์ ์ ๋ ฅ์ ์ฐ๊ฒฐํ์ฌ ๋ถ๊ธฐ๋๋ ์ค๋์ค ๊ฒฝ๋ก๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
AudioWorklet: ํ๋ก ํธ์๋์์ ์ฌ์ฉ์ ์ง์ DSP
๋งค์ฐ ๊น๋ค๋ก์ด ๋๋ ์ฌ์ฉ์ ์ง์ ๋์งํธ ์ ํธ ์ฒ๋ฆฌ(DSP) ์์
์ ๊ฒฝ์ฐ AudioWorklet API๋ ๋ณ๋์ ์ ์ฉ ์ค๋์ค ์ค๋ ๋์์ ์ฌ์ฉ์ ์ง์ JavaScript ์ฝ๋๋ฅผ ์คํํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ๋ณธ UI ์ค๋ ๋์์ ๊ฐ์ญ์ ๋ฐฉ์งํ๊ณ ๋ ๋ถ๋๋ฝ๊ณ ์์ธก ๊ฐ๋ฅํ ์ค๋์ค ์ฑ๋ฅ์ ๋ณด์ฅํฉ๋๋ค.
AudioWorklet์ ๋ ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
AudioWorkletProcessor: ์ค๋์ค ์ค๋ ๋์์ ์คํ๋๊ณ ์ค์ ์ค๋์ค ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ JavaScript ํด๋์ค์ ๋๋ค.AudioWorkletNode: ํ๋ก์ธ์์ ์ํธ ์์ฉํ๊ธฐ ์ํด ๊ธฐ๋ณธ ์ค๋ ๋์์ ๋ง๋๋ ์ฌ์ฉ์ ์ง์ ๋ ธ๋์ ๋๋ค.
๊ฐ๋ ์ ์์ (๊ฐ๋จํ๋จ):
my-processor.js(์ค๋์ค ์ค๋ ๋์์ ์คํ):
main.js(๊ธฐ๋ณธ ์ค๋ ๋์์ ์คํ):
AudioWorklet์ ๋ ๊ณ ๊ธ ์ฃผ์ ์ด์ง๋ง ์ฌ์ฉ์ ์ง์ ์๊ณ ๋ฆฌ์ฆ์ด ํ์ํ ์ฑ๋ฅ์ ์ค์ํ ์ค๋์ค ์ ํ๋ฆฌ์ผ์ด์
์ ํ์์ ์
๋๋ค.
์ค๋์ค ๋งค๊ฐ๋ณ์ ๋ฐ ์๋ํ
๋ง์ AudioNode์๋ ์ค์ ๋ก AudioParam ๊ฐ์ฒด(์: frequency, gain, delayTime)์ธ ์์ฑ์ด ์์ต๋๋ค. ์ด๋ฌํ ๋งค๊ฐ๋ณ์๋ ์๋ํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ฌ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์กฐ์ํ ์ ์์ต๋๋ค.
setValueAtTime(value, time): ํน์ ์๊ฐ์ ๋งค๊ฐ๋ณ์ ๊ฐ์ ์ค์ ํฉ๋๋ค.linearRampToValueAtTime(value, time): ์ง์ ๋ ๊ธฐ๊ฐ ๋์ ํ์ฌ ๊ฐ์์ ์ ๊ฐ์ผ๋ก ์ ํ ๋ณ๊ฒฝ์ ๋ง๋ญ๋๋ค.exponentialRampToValueAtTime(value, time): ๋ณผ๋ฅจ ๋๋ ํผ์น ๋ณ๊ฒฝ์ ์์ฃผ ์ฌ์ฉ๋๋ ์ง์ ๋ณ๊ฒฝ์ ๋ง๋ญ๋๋ค.setTargetAtTime(target, time, timeConstant): ์ง์ ๋ ์๊ฐ ์์๋ก ๋์ ๊ฐ์ผ๋ก์ ๋ณ๊ฒฝ์ ์์ฝํ์ฌ ๋ถ๋๋ฝ๊ณ ์์ฐ์ค๋ฌ์ด ์ ํ์ ๋ง๋ญ๋๋ค.start()๋ฐstop(): ๋งค๊ฐ๋ณ์ ์๋ํ ๊ณก์ ์ ์์๊ณผ ๋์ ์์ฝํฉ๋๋ค.
์ด๋ฌํ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํํ ์ ์ด ๋ฐ ๋ณต์กํ ์๋ฒจ๋กํ๊ฐ ๊ฐ๋ฅํ์ฌ ์ค๋์ค๋ฅผ ๋์ฑ ๋์ ์ด๊ณ ํํ๋ ฅ ์๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
์๊ฐํ: ์ค๋์ค์ ์๋ช ๋ถ์ด๋ฃ๊ธฐ
AnalyserNode๋ ์ค๋์ค ์๊ฐํ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ๊ฐ์ฅ ์นํ ์น๊ตฌ์
๋๋ค. ์ฃผํ์ ์์ญ ๋๋ ์๊ฐ ์์ญ์์ ์์ ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ์บก์ฒํ ์ ์์ต๋๋ค.
์์ : Canvas API๋ฅผ ์ฌ์ฉํ ๊ธฐ๋ณธ ์ฃผํ์ ์๊ฐํ
```javascript let analyser; let canvas; let canvasContext; function setupVisualizer(audioSource) { if (!audioContext) return; analyser = audioContext.createAnalyser(); analyser.fftSize = 2048; // 2์ ๊ฑฐ๋ญ์ ๊ณฑ์ด์ด์ผ ํฉ๋๋ค. const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); // ์์ค๋ฅผ ๋ถ์๊ธฐ์ ์ฐ๊ฒฐํ ๋ค์ ๋์์ผ๋ก ์ฐ๊ฒฐํฉ๋๋ค. audioSource.connect(analyser); analyser.connect(audioContext.destination); // ์บ๋ฒ์ค ์ค์ canvas = document.getElementById('audioVisualizer'); // ๊ฐ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. canvasContext = canvas.getContext('2d'); canvas.width = 600; canvas.height = 300; drawVisualizer(dataArray, bufferLength); } function drawVisualizer(dataArray, bufferLength) { requestAnimationFrame(() => drawVisualizer(dataArray, bufferLength)); analyser.getByteFrequencyData(dataArray); // ์ฃผํ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ canvasContext.clearRect(0, 0, canvas.width, canvas.height); canvasContext.fillStyle = 'rgb(0, 0, 0)'; canvasContext.fillRect(0, 0, canvas.width, canvas.height); const barWidth = (canvas.width / bufferLength) * 2.5; let x = 0; for(let i = 0; i < bufferLength; i++) { const barHeight = dataArray[i]; canvasContext.fillStyle = 'rgb(' + barHeight + ',50,50)'; canvasContext.fillRect(x, canvas.height - barHeight, barWidth, barHeight); x += barWidth + 1; } } // ์ฌ์ฉ ๋ฐฉ๋ฒ: // 'source'๊ฐ OscillatorNode ๋๋ BufferSourceNode๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. // setupVisualizer(source); // source.start(); ```fftSize ์์ฑ์ ๊ณ ์ ํธ๋ฆฌ์ ๋ณํ์ ์ฌ์ฉ๋๋ ์ํ ์๋ฅผ ๊ฒฐ์ ํ์ฌ ์ฃผํ์ ํด์๋์ ์ฑ๋ฅ์ ์ํฅ์ ์ค๋๋ค. frequencyBinCount๋ fftSize์ ์ ๋ฐ์
๋๋ค.
๋ชจ๋ฒ ์ฌ๋ก ๋ฐ ๊ณ ๋ ค ์ฌํญ
Web Audio API๋ฅผ ๊ตฌํํ ๋๋ ๋ค์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ผ๋์ ๋์ญ์์ค.
- `AudioContext` ์์ฑ์ ์ํ ์ฌ์ฉ์ ์ํธ ์์ฉ: ํญ์ ํด๋ฆญ ๋๋ ํญ๊ณผ ๊ฐ์ ์ฌ์ฉ์ ์ ์ค์ฒ์ ๋ํ ์๋ต์ผ๋ก
AudioContext๋ฅผ ๋ง๋์ญ์์ค. ์ด๋ ๋ธ๋ผ์ฐ์ ์๋ ์ฌ์ ์ ์ฑ ์ ์ค์ํ๊ณ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ณด์ฅํฉ๋๋ค. - ์ค๋ฅ ์ฒ๋ฆฌ: Web Audio API๊ฐ ์ง์๋์ง ์๊ฑฐ๋ ์ค๋์ค ๋์ฝ๋ฉ ๋๋ ์ฌ์์ด ์คํจํ๋ ๊ฒฝ์ฐ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
- ๋ฆฌ์์ค ๊ด๋ฆฌ:
BufferSourceNode์ ๊ฒฝ์ฐ ๋ ์ด์ ํ์ํ์ง ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธAudioBuffer๊ฐ ํด์ ๋์๋์ง ํ์ธํ์ฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ๋ณดํ์ญ์์ค. - ์ฑ๋ฅ: ํนํ
AudioWorklet์ ์ฌ์ฉํ ๋ ์ค๋์ค ๊ทธ๋ํ์ ๋ณต์ก์ฑ์ ์ ์ํ์ญ์์ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ํ๋กํ์ผ๋งํ์ฌ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํฉ๋๋ค. - ๊ต์ฐจ ๋ธ๋ผ์ฐ์ ํธํ์ฑ: ๋ค์ํ ๋ธ๋ผ์ฐ์ ๋ฐ ์ฅ์น์์ ์ค๋์ค ๊ตฌํ์ ํ ์คํธํฉ๋๋ค. Web Audio API๋ ์ ์ง์๋์ง๋ง ๋ฏธ๋ฌํ ์ฐจ์ด๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์ ๊ทผ์ฑ: ์ค๋์ค๋ฅผ ์ธ์ํ ์ ์๋ ์ฌ์ฉ์๋ฅผ ๊ณ ๋ คํ์ญ์์ค. ๋์ฒด ํผ๋๋ฐฑ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ๊ฑฐ๋ ์ค๋์ค๋ฅผ ๋นํ์ฑํํ๋ ์ต์ ์ ์ ๊ณตํฉ๋๋ค.
- ์ ์ญ ์ค๋์ค ํ์: ์ค๋์ค ํ์ผ์ ๋ฐฐํฌํ ๋๋ MP3 ๋๋ AAC์ ํจ๊ป ๋ ๋์ ํธํ์ฑ๊ณผ ๋ ๋์ ์์ถ์ ์ํด Ogg Vorbis ๋๋ Opus์ ๊ฐ์ ํ์์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ตญ์ ์ ์ธ ์์ ๋ฐ ์์ฉ ํ๋ก๊ทธ๋จ
Web Audio API๋ ๋ค์ฌ๋ค๋ฅํ๋ฉฐ ๋ค์ํ ๊ธ๋ก๋ฒ ์ฐ์ ์์ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ฐพ์ต๋๋ค.
- ๋ํํ ์์ ์์ฉ ํ๋ก๊ทธ๋จ: (Web Audio API ํตํฉ์ด ์๋) Ableton Link์ ๊ฐ์ ํ๋ซํผ์ ์ฌ์ฉํ๋ฉด ์ฅ์น์ ์์น์์ ํ์ ์์ ์ ๋ง๋ค ์ ์์ต๋๋ค.
- ๊ฒ์ ๊ฐ๋ฐ: ๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ ๊ฒ์์์ ์ฌ์ด๋ ํจ๊ณผ, ๋ฐฐ๊ฒฝ ์์ ๋ฐ ๋ฐ์ํ ์ค๋์ค ํผ๋๋ฐฑ์ ๋ง๋ญ๋๋ค.
- ๋ฐ์ดํฐ ์๋ํผ์ผ์ด์ : ๋ณต์กํ ๋ฐ์ดํฐ ์ธํธ(์: ๊ธ์ต ์์ฅ ๋ฐ์ดํฐ, ๊ณผํ์ ์ธก์ )๋ฅผ ๋ ์ฌ์ด ๋ถ์ ๋ฐ ํด์์ ์ํด ์ฌ์ด๋๋ก ํํํฉ๋๋ค.
- ํฌ๋ฆฌ์์ดํฐ๋ธ ์ฝ๋ฉ ๋ฐ ์ํธ ์ค์น: ์์ฑ ์์ , ์๊ฐ ์์ ์ ์ค์๊ฐ ์ค๋์ค ์กฐ์, ์น ๊ธฐ์ ๋ก ๊ตฌ๋๋๋ ๋ํํ ์ฌ์ด๋ ์ค์น. CSS Creatures์ ๊ฐ์ ์น์ฌ์ดํธ์ ๋ง์ ๋ํํ ์์ ํ๋ก์ ํธ๋ API๋ฅผ ํ์ฉํ์ฌ ๊ณ ์ ํ ์ฒญ๊ฐ์ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
- ์ ๊ทผ์ฑ ๋๊ตฌ: ์๊ฐ ์ฅ์ ๊ฐ ์๋ ์ฌ์ฉ์ ๋๋ ์๋๋ฌ์ด ํ๊ฒฝ์ ์ฌ์ฉ์๋ฅผ ์ํ ์ฒญ๊ฐ์ ํผ๋๋ฐฑ์ ๋ง๋ญ๋๋ค.
- ๊ฐ์ ๋ฐ ์ฆ๊ฐ ํ์ค: WebXR ๊ฒฝํ์์ ๊ณต๊ฐ ์ค๋์ค ๋ฐ ๋ชฐ์ ํ ์ฌ์ด๋์ค์ผ์ดํ๋ฅผ ๊ตฌํํฉ๋๋ค.
๊ฒฐ๋ก
Web Audio API๋ ํ๋ถํ๊ณ ๋ํํ ์ค๋์ค๋ก ์น ์ ํ๋ฆฌ์ผ์ด์
์ ํฅ์์ํค๋ ค๋ ๋ชจ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ฅผ ์ํ ๊ธฐ๋ณธ ๋๊ตฌ์
๋๋ค. ๊ฐ๋จํ ์ฌ์ด๋ ํจ๊ณผ์์ ๋ณต์กํ ํฉ์ฑ ๋ฐ ์ค์๊ฐ ์ฒ๋ฆฌ์ ์ด๋ฅด๊ธฐ๊น์ง ๊ทธ ๊ธฐ๋ฅ์ ๊ด๋ฒ์ํฉ๋๋ค. AudioContext, ์ค๋์ค ๋
ธ๋ ๋ฐ ๋ชจ๋์ ๊ทธ๋ํ ๊ตฌ์กฐ์ ํต์ฌ ๊ฐ๋
์ ์ดํดํ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ ์๋ก์ด ์ฐจ์์ ์ด ์ ์์ต๋๋ค. AudioWorklet๊ณผ ๋ณต์กํ ์๋ํ๋ฅผ ํตํด ์ฌ์ฉ์ ์ง์ DSP๋ฅผ ํ์ํ๋ฉด ์ง์ ์ผ๋ก ์ ์ธ๊ณ ๋์งํธ ์ฌ์ฉ์๋ฅผ ์ํ ์ต์ฒจ๋จ ์ค๋์ค ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
์คํ์ ์์ํ๊ณ ๋ ธ๋๋ฅผ ์ฐ๊ฒฐํ๊ณ ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ด๋ ์์ด๋์ด๋ฅผ ์คํํ์ญ์์ค!